package com.jbwz.web.security.service.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jbwz.web.common.base.BaseServiceImpl;
import com.jbwz.web.common.base.IBasePage;
import com.jbwz.web.common.exception.BusinessException;
import com.jbwz.web.common.res.ResponseCodeBase;
import com.jbwz.web.common.session.SecurityUserHolder;
import com.jbwz.web.common.session.SessionUser;
import com.jbwz.web.security.entity.Resource;
import com.jbwz.web.security.entity.User;
import com.jbwz.web.security.entity.UserRole;
import com.jbwz.web.security.entity.vo.UserVO;
import com.jbwz.web.security.enums.AdminFlagEnum;
import com.jbwz.web.security.mapper.UserMapper;
import com.jbwz.web.security.service.ResourceService;
import com.jbwz.web.security.service.UserRoleService;
import com.jbwz.web.security.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Stream;

/**
 * 用户 服务实现类
 *
 * @author yyh
 * @since 2021-07-16 23:46:06
 */
@Slf4j
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
  @Autowired private PasswordEncoder passwordEncoder;

  @Autowired private UserRoleService userRoleService;

  @Autowired private ResourceService resourceService;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = super.getOne(Wrappers.<User>query().eq("username", username));
    if (Objects.isNull(user)) {
      throw new UsernameNotFoundException("user not exist");
    }
    SessionUser sessionUser = toSessionUser(user);
    List<Long> roleIds = userRoleService.getByUserId(user.getId());
    sessionUser.setRoleIds(roleIds);
    sessionUser.setRequestMatcher(this.getCurrentUserResourceMatcher(sessionUser));
    return sessionUser;
  }

  private SessionUser toSessionUser(User user) {
    SessionUser sessionUser = new SessionUser();
    sessionUser.setId(user.getId());
    sessionUser.setUsername(user.getUsername());
    sessionUser.setPassword(user.getPassword());
    sessionUser.setAdminFlag(user.getAdminFlag());
    sessionUser.setCompanyId(user.getCompanyId());
    sessionUser.setEnabled(true);
    return sessionUser;
  }

  @Override
  public RequestMatcher getCurrentUserResourceMatcher(SessionUser user) {
    List<Resource> list = resourceService.getCurrentUserResource(user);
    List<RequestMatcher> requestMatchers =
        new ArrayList<>(
            list.stream()
                .flatMap(
                    r -> {
                      List<AntPathRequestMatcher> ra = new ArrayList<>();
                      if (StringUtils.isNotBlank(r.getPath())) {
                        ra.add(new AntPathRequestMatcher(r.getPath()));
                      }
                      String dependencyPath = r.getDependencyPath();
                      if (StringUtils.isNotBlank(dependencyPath)) {
                        String[] split = dependencyPath.split(",");
                        for (String s : split) {
                          if (StringUtils.isNotBlank(s)) {
                            ra.add(new AntPathRequestMatcher(s));
                          }
                        }
                      }
                      return Stream.of(ra.toArray(new AntPathRequestMatcher[0]));
                    })
                .toList());
    // 保证必须得有一个元素
    requestMatchers.add(new AntPathRequestMatcher("/login"));
    return new OrRequestMatcher(requestMatchers);
  }

  /**
   * 获取当前用户的所有资源权限,供界面判断
   *
   * @return
   */
  @Override
  public Set<String> getCurrentUserPKey() {
    SessionUser user = SecurityUserHolder.getCurrentUser();
    Set<String> list = resourceService.getCurrentUserResourcePkey(user);
    return list;
  }

  @Override
  public void saveByReg(User user) {
    checkUsername(user.getId(), user.getUsername());
    user.setPassword(passwordEncoder.encode(user.getPassword()));
    save(user);
  }

  private void checkUsername(Long userId, String username) {
    Optional<User> byName = getByUsername(username);
    if (byName.isPresent()) {
      User user = byName.get();
      if (Objects.isNull(userId) || !user.getId().equals(userId)) {
        throw BusinessException.resMsg(ResponseCodeBase.dataExist.withMsg("用户名被占用"));
      }
    }
  }

  public Optional<User> getByUsername(String name) {
    User user = this.getOne(Wrappers.<User>query().eq("username", name));
    return Optional.ofNullable(user);
  }

  @Override
  public IBasePage pageList(IBasePage page, UserVO vo) {
    vo.setCompanyId(SecurityUserHolder.getCurrentCompanyId());
    return baseMapper.pageList(page, vo);
  }

  private User toEntity(UserVO vo) {
    User user = new User();
    BeanUtils.copyProperties(vo, user);
    if (Objects.nonNull(vo.getPassword())) {
      String encode = passwordEncoder.encode(vo.getPassword());
      user.setPassword(encode);
    }
    user.setCompanyId(SecurityUserHolder.getCurrentCompanyId());
    return user;
  }

  @Override
  public void removeByIdCheck(Long id) {
    User user = getById(id);
    Integer adminFlag = user.getAdminFlag();
    if (AdminFlagEnum.ADMIN.getCode().equals(adminFlag)
        || AdminFlagEnum.SUPER_ADMIN.getCode().equals(adminFlag)) {
      return;
    }
    this.removeById(id);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public void saveOrUpdateVO(UserVO vo) {
    checkUsername(vo.getId(), vo.getUsername());
    User user = toEntity(vo);
    //不能更新管理员标识
    user.setAdminFlag(null);
    saveOrUpdate(user);
    List<Long> roleIds = vo.getRoleIds();
    userRoleService.delByUserId(user.getId());
    userRoleService.saveBatch(
        roleIds.stream()
            .map(
                rid -> {
                  UserRole userRole = new UserRole();
                  userRole.setUserId(user.getId());
                  userRole.setRoleId(rid);
                  return userRole;
                })
            .toList());
  }
}
